/*#################################################

Keycode | v1.1 
Max Felker | max@bigroomstudios.com

Keycode event observation and grouping for humans

Example:

// single
live.keys.look_for("ctrl", function() {
   live.modal.open({src:'/admin'})
});

// combo
live.keys.look_for("ctrl+home", function() {
   live.modal.open({src:'/admin'})
});

// range
live.keys.look_for("alpha", function() {
   live.modal.open({src:'/admin'})
});

// range combo
live.keys.look_for("alpha+numeric", function() {
   live.modal.open({src:'/admin'})
});

#################################################*/

// START Class
var Keys = Class.create({

	initialize: function() {
		
		// key code index
		this.index = $H({	
			
			// non alphanum
			8: "backspace",
			9: "tab",
			13: "enter",
			16: "shift",
			17: "ctrl",
			18: "alt",
			19: "pause_break",
			20: "caps",
			27: "esc",
			32: "space",
			33: "page_up",
			34: "page_down",
			35: "end",		
			36: "home",
			37: "arrow_left",
			38: "arrow_up",
			39: "arrow_right",
			40: "arrow_down",
			45: "insert",
			46: "delete",
			91: "windows_key_left",
			92: "windows_key_right",
			93: "select",
			144: "num_lock",
			145: "scroll_lock",
			224: "apple_key_left",
			224: "apple_key_right",		
			
			// chars
			186: "semicolon", 
			187: "equals",
			188: "comma",
			189: "hyphen", 
			190: "period",
			191: "slash_forward",
			192: "grave_accent",
			219: "bracket_open",
			220: "slash_back",
			221: "bracket_close",
			222: "quote_single",
			
			// numbers
			48: "0",
			49: "1",
			50: "2",
			51: "3",
			52: "4",
			53: "5",
			54: "6",
			55: "7",
			56: "8",
			57: "9",

			// alpha
			65: "a",
			66: "b",
			67: "c",
			68: "d",
			69: "e",
			70: "f",
			71: "g",
			72: "h",
			73: "i",
			74: "j",
			75: "k",
			76: "l",
			77: "m",
			78: "n",
			79: "o",
			80: "p",
			81: "q",
			82: "r",
			83: "s",
			84: "t",
			85: "u",
			86: "v",
			87: "w",
			88: "x",
			89: "y",
			90: "z",
			
			//numpad
			96: "numpad_0",
			97: "numpad_1",
			98: "numpad_2",
			99: "numpad_3",
			100: "numpad_4",
			101: "numpad_5",
			102: "numpad_6",
			103: "numpad_7",
			104: "numpad_8",
			105: "numpad_9",
			106: "multipy",
			107: "plus", 
			109: "minus", 
			110: "decimal",
			111: "divide",

			//fkeys
			112: "f1",
			113: "f2",
			114: "f3",
			115: "f4",
			116: "f5",
			117: "f6",
			118: "f7",
			119: "f8",
			120: "f9",
			121: "f10",
			122: "f11",
			123: "f12"
			
				
		});
		
		// ranges for watching sets of keys
		this.range = $A();
		this.range['numeric'] = $R(49,57);
		this.range['alpha'] = $R(65, 90);
		this.range['chars'] = $R(186,222);
		this.range['numpad'] = $R(96,111);
		this.range['fkeys'] = $R(112,123);
		
		this.browser_mod = false;
		
		// firefox mods
		if(Prototype.Browser.Gecko) {
		
			this.browser_mod = 'gecko';
		
			this.gecko_index = $H({
				59: 'semicolon',
				61: 'equals',
				109: 'hyphen'
			});	
			
			this.range['chars']['gecko'] = $A();
			this.range['chars']['gecko'][59] = true;
			this.range['chars']['gecko'][61] = true;
			this.range['chars']['gecko'][109] = true;
				
			this.index.unset('186');
			this.index.unset('187');
			this.index.unset('189');
			
		}
		
		// key groups hash to iterate through on key up
		this.groups = $H();
		
		this.reset_key_values();
		
		// watch the key down
		document.on('keydown',function(event) {
		
			// set previous key code and value
			this.key_code.previous = this.key_code.current;
			this.key_value.previous = this.key_value.current;
			
			// set current key code and value
			this.key_code.current = event.keyCode;	
			this.key_value.current = this.index.get(this.key_code.current);
			
			// if the current value wasn't found in the index
			if(Object.isUndefined(this.key_value.current)) {
			
				// check the gecko index
				if(this.browser_mod=='gecko') {
					this.key_value.current = this.gecko_index.get(this.key_code.current);
				}
			
			}
			
		}.bind(this));

	},
	
	reset_key_values: function() {
	
		this.key_code = {
			current: '',
			previous: ''
		}
		
		this.key_value = {
			current: '',
			previous: ''
		}
	},
	
	look_for: function(key_combo,user_function,poll_match) {
	    
	    if(!poll_match) {
	    	poll_match = false;
		}
		
	    key_combo_config = {
	    	keys: key_combo,
	    	keys_array: key_combo.split("+"),
	    	user_function: user_function,
	    	poll_match: poll_match
	    };
	    
	    this.groups.set(key_combo,key_combo_config);
	    
		this.match_keys_handler = this.match_keys.bind(this);
		
		document.on('keyup', this.match_keys_handler);
	
	},
	
	stop_looking_for: function(key_combo) {
	
		this.groups.unset(key_combo);
	
	},

				// if key 2 is a range
	match_keys: function() {
	
		// if there are no groups, exit
		if(this.groups.size()==0) {
			return false;
		} 
	
		// each key group
		this.groups.values().each(function(key_group) {
		
			var keys_matched = false;
			
			var key_1 = key_group.keys_array[0];
			var key_2 = key_group.keys_array[1]; 
			
			// check for two keys
			if(key_2) {
			
				// if key 2 is a range
				if(!Object.isUndefined(this.range[key_2])) {
			
					if(this.range[key_2].include(this.key_code.current)
					||this.range[key_2][this.browser_mod] &&  this.range[key_2][this.browser_mod][this.key_code.current]) {
				
						this.key_value.current = this.index.get(this.key_code.current);
					
						if(Object.isUndefined(this.key_value.current)) {
			
							// check the gecko index
							if(this.browser_mod=='gecko') {
								this.key_value.current = this.gecko_index.get(this.key_code.current);
							}
			
						}
					
						key_2 = this.key_value.current;
					}
				
				}
				
				// if key 1 is a range
				if(!Object.isUndefined(this.range[key_1])) {
				
					if(this.range[key_1].include(this.key_code.previous)
					|| this.range[key_1][this.browser_mod] &&  this.range[key_1][this.browser_mod][this.key_code.previous]) {
				
						this.key_value.previous = this.index.get(this.key_code.previous);
					
						if(Object.isUndefined(this.key_value.previous)) {
			
							// check the gecko index
							if(this.browser_mod=='gecko') {
								this.key_value.previous = this.gecko_index.get(this.key_code.previous);
							}
			
						}
					
						key_1 = this.key_value.previous;
						
					}
					
				}
				
			    
			    // if key 2 = current key && key 1 = previous key
			    if(this.key_value.previous==key_1 && this.key_value.current==key_2) {
			        keys_matched = true;
			    } 
			    
			// if only one key
			}else if(key_1) {
		    
		    	// if key 1 is a range
		    	if(!Object.isUndefined(this.range[key_1])) {
		    	
					if(this.range[key_1].include(this.key_code.current)
					|| this.range[key_1][this.browser_mod] && this.range[key_1][this.browser_mod][this.key_code.current]) {
					
						this.key_value.current = this.index.get(this.key_code.current);
						
						if(Object.isUndefined(this.key_value.current)) {
			
							// check the gecko index
							if(this.browser_mod=='gecko') {
								this.key_value.current = this.gecko_index.get(this.key_code.current);
							}
			
						}
						
						key_1 = this.key_value.current;
			   	 	} 
		   	 	
		   	 	}
		        
		        // if key 1 = current key
		        if(this.key_value.current==key_1) {
		            keys_matched = true;
		        } 
		        
		    }
		    
		    // if the keys matched
			if(keys_matched) {
				
				// set function and eval
				live.key_down_user_function = '';
				live.key_down_user_function = key_group.user_function.methodize();
				live.key_down_user_function();
			
				// set key codes and valeus back to empty
				this.reset_key_values();
	   			
	   			// if !poll, unset the key group
				if(!key_group.poll_match) {
			   		this.groups.unset(key_group.keys);
				}
			
			}	 

		}.bind(this)); // end each key group
		
	} // EMD match_keys()

}); // END Class 

